# -*- coding: utf-8 -*-
import time
from datetime import date, datetime, timedelta
import re
from .weixin import WXAPPAPI
from .weixin.oauth2 import OAuth2AuthExchangeError
from config.settings import settings
import pytz
import string
import random
import hashlib
import logging
from .error_code import error_code

DATE_FORMAT = "%Y-%m-%d"
TIME_FORMAT = "%H:%M:%S"
DATETIME_FORMAT = "%s %s" % (DATE_FORMAT, TIME_FORMAT)
DATE_LENGTH = len(date.today().strftime(DATE_FORMAT))
DATETIME_LENGTH = len(datetime.now().strftime(DATETIME_FORMAT))

_logger = logging.getLogger(__name__)


def getDays(day):
    # 获取需要计算的时间戳
    time_array = time.strptime(day, '%Y-%m-%d')
    timestamp = int(time.mktime(time_array))
    # 获取今天的时间戳
    time_array2 = time.strptime(datetime.date.today().__str__(), '%Y-%m-%d')
    timestamp2 = int(time.mktime(time_array2))
    if timestamp2 < timestamp:
        timestamp2, timestamp = timestamp, timestamp2
    # 时间戳相减，然后算出天数
    day = int((timestamp2 - timestamp) / (24 * 60 * 60))
    return day


def context_today(timestamp=None):
    """ Return the current date as seen in the client's timezone in a format
        fit for date fields. This method may be used to compute default
        values.

        :param datetime timestamp: optional datetime value to use instead of
            the current date and time (must be a datetime, regular dates
            can't be converted between timezones.)
        :rtype: str
    """
    today = timestamp or datetime.now()
    context_today = None
    tz_name = settings.CLIENT_TZ
    if tz_name:
        try:
            today_utc = pytz.timezone('UTC').localize(today, is_dst=False)  # UTC = no DST
            context_today = today_utc.astimezone(pytz.timezone(tz_name))
        except Exception:
            _logger.debug("failed to compute context/client-specific today date, using UTC value for `today`",
                          exc_info=True)
    return (context_today or today).strftime(DATE_FORMAT)


def from_string(value):
    """ Convert an ORM ``value`` into a :class:`date` value. """
    if not value:
        return None
    value = value[:DATE_LENGTH]
    return datetime.strptime(value, DATE_FORMAT).date()


def to_string(value):
    """ Convert a :class:`date` value into the format expected by the ORM. """
    return value.strftime(DATE_FORMAT) if value else False


def get_context_year():
    """获取客户端时区的当前年"""
    return from_string(context_today()).strftime('%Y')


def get_context_month():
    """获取客户端时区的当前月份"""
    month = from_string(context_today()).strftime('%m')
    month = str(int(month))
    return month


def get_context_day():
    """获取客户端时区的当前日"""
    day = from_string(context_today()).strftime('%d')
    day = str(int(day))
    return day


def get_current_week():
    now_week = from_string(context_today()).weekday()
    # 当前日期 - now_week天 为本周一的日期
    start_day = from_string(context_today(datetime.fromtimestamp(datetime.now().timestamp() - now_week * 24 * 60 * 60)))
    return str(from_string(context_today())), str(start_day)


def context_timestamp(timestamp=None):
    """Returns the given timestamp converted to the client's timezone.
       This method is *not* meant for use as a default initializer,
       because datetime fields are automatically converted upon
       display on client side. For default values :meth:`fields.datetime.now`
       should be used instead.

       :param datetime timestamp: naive datetime value (expressed in UTC)
                                  to be converted to the client timezone
       :rtype: datetime
       :return: timestamp converted to timezone-aware datetime in context
                timezone
    """
    timestamp = timestamp or datetime.now()
    tz_name = settings.CLIENT_TZ
    utc_timestamp = pytz.utc.localize(timestamp, is_dst=False)  # UTC = no DST
    if tz_name:
        try:
            context_tz = pytz.timezone(tz_name)
            return (utc_timestamp.astimezone(context_tz) or timestamp).strftime(DATETIME_FORMAT)
        except Exception:
            _logger.debug("failed to compute context/client-specific timestamp, "
                          "using the UTC value",
                          exc_info=True)
    return utc_timestamp


def getSign(transactionId):
    md5 = hashlib.md5()
    md5.update((transactionId + settings.SECRET_KEY).encode("utf8"))
    return md5.hexdigest()


def generate_nonce_str(length=32):
    return ''.join(random.SystemRandom().choice(
        string.ascii_letters + string.digits) for _ in range(length))


"""类转字典"""


def class_to_dict(obj):
    result = {}
    for name in dir(obj):
        value = getattr(obj, name)
        if not name.startswith('__') and not callable(value):
            result[name] = value
    return result


def get_params(request):
    params = {}
    headers = request.headers
    method = request.method
    if method == "GET":
        params = request.args
    elif method in ("POST", "PUT"):
        content_type = headers.get('Content-Type')
        if "www-form" in content_type:  # 表单请求
            params = request.form
        elif "application/json" in content_type:  # json请求
            params = request.get_json()
    return params


#  必填参数校验
def required_parame_verify(req_params, params):
    '''
    params_ = params 
    if instance(params,list):
        params_ = {}
        for param in params:
            params_[param[1]]=param[-1]
    '''
    for parame in req_params:
        try:
            if parame not in params.keys():
                return {'code': 300, 'msg': error_code[300].format(parame)}
            if not params.get(parame):
                return {'code': 413, 'msg': error_code[413].format(parame)}
        except Exception as e:
            return {'code': 412, 'msg': error_code[412]}
    return {'code': 0, 'msg': 'success', 'data': {}, 'success': True}


def num_to_char(num):
    """数字转中文"""
    num_dict = {"0": u"零", "1": u"一", "2": u"二", "3": u"三", "4": u"四", "5": u"五", "6": u"六", "7": u"七", "8": u"八", "9": u"九"}
    num = str(num)
    new_str = ""
    listnum = list(num)
    shu = []
    for i in listnum:
        shu.append(num_dict[i])
    new_str = "".join(shu)
    return new_str


#  计算年龄
def calculate_age(born):
    birthDate = datetime.strptime(born, "%d/%m/%Y").date()
    currentDate = datetime.today().date()
    age = currentDate.year - birthDate.year
    monthVeri = currentDate.month - birthDate.month
    dateVeri = currentDate.day - birthDate.day
    age = int(age)
    monthVeri = int(monthVeri)
    dateVeri = int(dateVeri)
    if monthVeri < 0:
        age = age - 1
    elif dateVeri < 0 and monthVeri == 0:
        age = age - 1
    return age


# 通过时间 计算差额时间戳（描述）
# start开始时间 end 截止时间
def diff_datetime_count(start, end):
    timearray_start = time.strptime(start, "%Y-%m-%d %H:%M:%S")
    timearray_end = time.strptime(end, "%Y-%m-%d %H:%M:%S")
    return int(time.mktime(timearray_end) - time.mktime(timearray_start))


# 通过日期 计算差额日期（描述）
# start开始日期 end 截止日期(不传end取今天)
def diff_date_count(start, end=0):
    timearray_start = time.strptime(start, "%Y-%m-%d")
    start_ = time.mktime(timearray_start)
    end_ = time.time()
    if end:
        timearray_end = time.strptime(end, "%Y-%m-%d")
        end_ = time.mktime(timearray_end)
    return int(int(end_ - start_) / 60 / 60 / 24)


#  获取当前周
def get_current_week(date=None):
    if date:
        duty_date = datetime.strptime(str(date), '%Y-%m-%d')
        monday, sunday = duty_date, duty_date
    else:
        monday, sunday = datetime.today(), datetime.today()
    one_day = timedelta(days=1)
    while monday.weekday() != 0:
        monday -= one_day
    while sunday.weekday() != 6:
        sunday += one_day

    # return monday, sunday
    # 返回时间字符串
    return datetime.strftime(monday, "%Y-%m-%d"), datetime.strftime(sunday, "%Y-%m-%d")


def datetime_compare(first_time, second_time):
    """日期时间类型比较，参数为日期类型"""
    return int(datetime.strftime(first_time, "%Y%m%d%H%M%S")) - int(datetime.strftime(second_time, "%Y%m%d%H%M%S"))


def snake_case_to_camel(key: str, first_upper=False):
    """字符串蛇形转驼峰"""
    items = key.split('_')
    ret = ''
    for item in items:
        if not item: continue
        first_item = item[0]
        if not (len(ret) == 0 and first_upper == False):
            first_item = first_item.upper()
        item = f'{first_item}{item[1:]}'
        ret += item
    return ret


def snake_case_upper(key: str):
    """字符串蛇形转大写蛇形"""
    items = key.split('_')
    ret = ''
    for i, item in enumerate(items):
        if not item: continue
        first_item = item[0].upper()
        item = f'{first_item}{item[1:]}'
        if i == 0:
            ret += item
        else:
            ret += f"_{item}"
    return ret


def camel_to_snake_case(text):
    """字符串驼峰转蛇形"""
    text = text.replace('_', '')
    matches = re.finditer('[A-Z]', text)
    contents = []
    last_start = 0
    for it in matches:
        start, end = it.span()
        if start > 0:
            contents.append(text[last_start:start])
        last_start = start
    contents.append(text[last_start:])
    return '_'.join(contents).lower()


def dict_snake_case_to_camel(data: dict, first_upper=False):
    """字典蛇形转驼峰（递归处理嵌套结构）"""
    if isinstance(data, dict):
        new_data = {}
        for key, value in data.items():
            # 转换当前键名
            new_key = snake_case_to_camel(key, first_upper)
            # 递归处理值
            if isinstance(value, dict):
                new_data[new_key] = dict_snake_case_to_camel(value, first_upper)
            elif isinstance(value, list):
                new_data[new_key] = [dict_snake_case_to_camel(item, first_upper) if isinstance(item, dict) else item for item in value]
            else:
                new_data[new_key] = value
        return new_data
    elif isinstance(data, list):
        return [dict_snake_case_to_camel(item, first_upper) if isinstance(item, dict) else item for item in data]
    else:
        return data


def dict_snake_case_upper(data: dict):
    """字典蛇形转大写蛇形（递归处理嵌套结构）"""
    if isinstance(data, dict):
        new_data = {}
        for key, value in data.items():
            # 转换当前键名
            new_key = snake_case_upper(key)
            # 递归处理值
            if isinstance(value, dict):
                new_data[new_key] = dict_snake_case_upper(value)
            elif isinstance(value, list):
                new_data[new_key] = [dict_snake_case_upper(item) if isinstance(item, dict) else item for item in value]
            else:
                new_data[new_key] = value
        return new_data
    elif isinstance(data, list):
        return [dict_snake_case_upper(item) if isinstance(item, dict) else item for item in data]
    else:
        return data



def get_wechat_session_info(app_id, secret, code):
    """
    :param app_id: 微信Appid
    :param secret: 微信secret
    :param code: 调用 wx.login 返回的code
    :return: session_info
    """
    api = WXAPPAPI(appid=app_id, app_secret=secret)
    try:
        # 使用 code 换取 session key
        session_info = api.exchange_code_for_session_key(code=code)
    except OAuth2AuthExchangeError as e:
        _logger.exception(e)
        raise e
    return session_info

def get_wechat_user_phone(app_id, secret, access_token, phone_code):
    """
    :param code: 用户授权获取手机号码返回的code
    :return: phone_number：用户手机号码
    """
    api = WXAPPAPI(appid=app_id, app_secret=secret)
    try:
        # 使用 code 换取 user phone number
        phone = api.exchange_code_for_phone(access_token=access_token, phone_code=phone_code)
        phone_number = phone.get('phone_info')['phoneNumber']
    except OAuth2AuthExchangeError as e:
        _logger.exception(e)
        raise e
    return phone_number

def clear_redis_cache(r):
    # 使用 SCAN 命令迭代所有键
    cursor = '0'
    while cursor != 0:
        cursor, keys = r.scan(cursor=cursor)
        if keys:
            # 使用管道批量删除
            with r.pipeline() as pipe:
                pipe.delete(*keys)
                pipe.execute()
    return True